﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BspFileFormat.Math
{
		/// <summary>
		/// Contains common mathematical functions and constants.
		/// </summary>
		public static class MathHelper
		{
			#region Fields

			/// <summary>
			/// Defines the value of Pi as a <see cref="System.Single"/>.
			/// </summary>
			public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f;

			/// <summary>
			/// Defines the value of Pi divided by two as a <see cref="System.Single"/>.
			/// </summary>
			public const float PiOver2 = Pi / 2;

			/// <summary>
			/// Defines the value of Pi divided by three as a <see cref="System.Single"/>.
			/// </summary>
			public const float PiOver3 = Pi / 3;

			/// <summary>
			/// Definesthe value of  Pi divided by four as a <see cref="System.Single"/>.
			/// </summary>
			public const float PiOver4 = Pi / 4;

			/// <summary>
			/// Defines the value of Pi divided by six as a <see cref="System.Single"/>.
			/// </summary>
			public const float PiOver6 = Pi / 6;

			/// <summary>
			/// Defines the value of Pi multiplied by two as a <see cref="System.Single"/>.
			/// </summary>
			public const float TwoPi = 2 * Pi;

			/// <summary>
			/// Defines the value of Pi multiplied by 3 and divided by two as a <see cref="System.Single"/>.
			/// </summary>
			public const float ThreePiOver2 = 3 * Pi / 2;

			/// <summary>
			/// Defines the value of E as a <see cref="System.Single"/>.
			/// </summary>
			public const float E = 2.71828182845904523536f;

			/// <summary>
			/// Defines the base-10 logarithm of E.
			/// </summary>
			public const float Log10E = 0.434294482f;

			/// <summary>
			/// Defines the base-2 logarithm of E.
			/// </summary>
			public const float Log2E = 1.442695041f;

			#endregion

			#region Public Members

			#region NextPowerOfTwo

			/// <summary>
			/// Returns the next power of two that is larger than the specified number.
			/// </summary>
			/// <param name="n">The specified number.</param>
			/// <returns>The next power of two.</returns>
			public static long NextPowerOfTwo(long n)
			{
				if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
				return (long)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
			}

			/// <summary>
			/// Returns the next power of two that is larger than the specified number.
			/// </summary>
			/// <param name="n">The specified number.</param>
			/// <returns>The next power of two.</returns>
			public static int NextPowerOfTwo(int n)
			{
				if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
				return (int)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
			}

			/// <summary>
			/// Returns the next power of two that is larger than the specified number.
			/// </summary>
			/// <param name="n">The specified number.</param>
			/// <returns>The next power of two.</returns>
			public static float NextPowerOfTwo(float n)
			{
				if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
				return (float)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
			}

			/// <summary>
			/// Returns the next power of two that is larger than the specified number.
			/// </summary>
			/// <param name="n">The specified number.</param>
			/// <returns>The next power of two.</returns>
			public static double NextPowerOfTwo(double n)
			{
				if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
				return System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
			}

			#endregion

			#region Factorial

			/// <summary>Calculates the factorial of a given natural number.
			/// </summary>
			/// <param name="n">The number.</param>
			/// <returns>n!</returns>
			public static long Factorial(int n)
			{
				long result = 1;

				for (; n > 1; n--)
					result *= n;

				return result;
			}

			#endregion

			#region BinomialCoefficient

			/// <summary>
			/// Calculates the binomial coefficient <paramref name="n"/> above <paramref name="k"/>.
			/// </summary>
			/// <param name="n">The n.</param>
			/// <param name="k">The k.</param>
			/// <returns>n! / (k! * (n - k)!)</returns>
			public static long BinomialCoefficient(int n, int k)
			{
				return Factorial(n) / (Factorial(k) * Factorial(n - k));
			}

			#endregion

			#region InverseSqrtFast

			///// <summary>
			///// Returns an approximation of the inverse square root of left number.
			///// </summary>
			///// <param name="x">A number.</param>
			///// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
			///// <remarks>
			///// This is an improved implementation of the the method known as Carmack's inverse square root
			///// which is found in the Quake III source code. This implementation comes from
			///// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
			///// http://www.beyond3d.com/content/articles/8/
			///// </remarks>
			//public static float InverseSqrtFast(float x)
			//{
			//    unsafe
			//    {
			//        float xhalf = 0.5f * x;
			//        int i = *(int*)&x;              // Read bits as integer.
			//        i = 0x5f375a86 - (i >> 1);      // Make an initial guess for Newton-Raphson approximation
			//        x = *(float*)&i;                // Convert bits back to float
			//        x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step.
			//        return x;
			//    }
			//}

			/// <summary>
			/// Returns an approximation of the inverse square root of left number.
			/// </summary>
			/// <param name="x">A number.</param>
			/// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
			/// <remarks>
			/// This is an improved implementation of the the method known as Carmack's inverse square root
			/// which is found in the Quake III source code. This implementation comes from
			/// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
			/// http://www.beyond3d.com/content/articles/8/
			/// </remarks>
			public static double InverseSqrtFast(double x)
			{
				return InverseSqrtFast((float)x);
				// TODO: The following code is wrong. Fix it, to improve precision.
#if false
            unsafe
            {
                double xhalf = 0.5f * x;
                int i = *(int*)&x;              // Read bits as integer.
                i = 0x5f375a86 - (i >> 1);      // Make an initial guess for Newton-Raphson approximation
                x = *(float*)&i;                // Convert bits back to float
                x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step.
                return x;
            }
#endif
			}

			#endregion

			#region DegreesToRadians

			/// <summary>
			/// Convert degrees to radians
			/// </summary>
			/// <param name="degrees">An angle in degrees</param>
			/// <returns>The angle expressed in radians</returns>
			public static float DegreesToRadians(float degrees)
			{
				const float degToRad = (float)System.Math.PI / 180.0f;
				return degrees * degToRad;
			}

			/// <summary>
			/// Convert radians to degrees
			/// </summary>
			/// <param name="radians">An angle in radians</param>
			/// <returns>The angle expressed in degrees</returns>
			public static float RadiansToDegrees(float radians)
			{
				const float radToDeg = 180.0f / (float)System.Math.PI;
				return radians * radToDeg;
			}

			#endregion

			#region Swap

			/// <summary>
			/// Swaps two double values.
			/// </summary>
			/// <param name="a">The first value.</param>
			/// <param name="b">The second value.</param>
			public static void Swap(ref double a, ref double b)
			{
				double temp = a;
				a = b;
				b = temp;
			}

			/// <summary>
			/// Swaps two float values.
			/// </summary>
			/// <param name="a">The first value.</param>
			/// <param name="b">The second value.</param>
			public static void Swap(ref float a, ref float b)
			{
				float temp = a;
				a = b;
				b = temp;
			}

			#endregion

			#endregion
		}
}
